---
title: Bar - Linked Brushing
---

```jsx live noInline
const barData = [
  {
    name: "SEA",
    range: [
      new Date(2013, 1, 1),
      new Date(2019, 1, 1),
    ],
  },
  {
    name: "HKG",
    range: [
      new Date(2015, 1, 1),
      new Date(2015, 5, 1),
    ],
  },
  {
    name: "LHR",
    range: [
      new Date(2016, 5, 1),
      new Date(2019, 1, 1),
    ],
  },
  {
    name: "DEN",
    range: [
      new Date(2018, 8, 1),
      new Date(2019, 1, 1),
    ],
  },
];

const pointData = [
  {
    name: "SEA",
    date: new Date(2012, 9, 1),
  },
  {
    name: "HKG",
    date: new Date(2014, 3, 1),
  },
  {
    name: "LHR",
    date: new Date(2015, 6, 1),
  },
  {
    name: "DEN",
    date: new Date(2018, 3, 1),
  },
];

const containerStyle = {
  display: "flex",
  flexDirection: "row",
  flexWrap: "wrap",
  alignItems: "center",
  justifyContent: "center",
  paddingBottom: 50
};

const domain = {
  y: [
    new Date(2012, 1, 1),
    new Date(2019, 1, 1),
  ],
  x: [0.5, 4.5],
};

const sharedProps = {
  width: 800,
  domain,
};

class DraggablePoint extends React.Component {
  static defaultEvents = [
    {
      target: "data",
      eventHandlers: {
        onMouseOver: (
          evt,
          targetProps,
        ) => {
          return [
            {
              mutation: () =>
                Object.assign(
                  targetProps,
                  { active: true },
                ),
            },
          ];
        },
        onMouseDown: (
          evt,
          targetProps,
        ) => {
          return [
            {
              mutation: () =>
                Object.assign(
                  targetProps,
                  { dragging: true },
                ),
            },
          ];
        },
        onMouseMove: (
          evt,
          targetProps,
        ) => {
          const {
            onPointChange,
            datum,
            scale,
          } = targetProps;

          if (targetProps.dragging) {
            const { x } =
              Selection.getSVGEventCoordinates(
                evt,
              );
            const point =
              scale.y.invert(x);
            const name = datum.name;

            onPointChange({
              name,
              date: point,
            });

            return [
              {
                mutation: () =>
                  Object.assign(
                    targetProps,
                    { x },
                  ),
              },
            ];
          }
          return null;
        },
        onMouseUp: (
          evt,
          targetProps,
        ) => {
          return [
            {
              mutation: () =>
                Object.assign(
                  targetProps,
                  {
                    dragging: false,
                    active: false,
                  },
                ),
            },
          ];
        },
        onMouseLeave: (
          evt,
          targetProps,
        ) => {
          return [
            {
              mutation: () =>
                Object.assign(
                  targetProps,
                  {
                    dragging: false,
                    active: false,
                  },
                ),
            },
          ];
        },
      },
    },
  ];

  render() {
    return <Point {...this.props} />;
  }
}

function App() {
  const [zoomDomain, setZoomDomain] =
    React.useState({});
  const [bars, setBars] =
    React.useState(barData);
  const [points, setPoints] =
    React.useState(pointData);

  function handleZoom(domain) {
    setZoomDomain(domain);
  }

  function onDomainChange(
    domain,
    props,
  ) {
    const { name } = props;
    const newBars = bars.map((bar) =>
      bar.name === name
        ? { name, range: domain }
        : bar,
    );

    setBars(newBars);
  }

  function onPointChange(point) {
    const newPoints = points.map((p) =>
      p.name === point.name ? point : p,
    );
    setPoints(newPoints);
  }

  return (
    <div style={containerStyle}>
      <VictoryChart
        theme={VictoryTheme.clean}
        horizontal
        {...sharedProps}
        containerComponent={
          <VictoryZoomContainer
            responsive={false}
            allowPan={false}
            zoomDomain={zoomDomain}
            zoomDimension="y"
            onZoomDomainChange={
              handleZoom
            }
            clipContainerComponent={
              <VictoryClipContainer
                clipPadding={{
                  top: 15,
                  bottom: 15,
                }}
              />
            }
          />
        }
      >
        <VictoryAxis
          style={{
            axis: { stroke: "none" },
          }}
        />

        {bars.map((bar, index) => (
          <VictoryAxis
            dependentAxis
            key={index}
            axisComponent={
              <VictoryBrushLine
                name={bar.name}
                width={20}
                allowDraw={false}
                brushDomain={bar.range}
                onBrushDomainChange={
                  onDomainChange
                }
                brushStyle={{
                  fill: VictoryTheme
                    .clean.palette
                    ?.colors?.cyan,
                  opacity: ({
                    active,
                  }) =>
                    active ? 1 : 0.5,
                }}
              />
            }
            style={{
              axis: { stroke: "none" },
            }}
            axisValue={bar.name}
            tickFormat={() => ""}
          />
        ))}
        <VictoryScatter
          data={points}
          dataComponent={
            <DraggablePoint
              onPointChange={
                onPointChange
              }
            />
          }
          style={{
            data: {
              fill: VictoryTheme.clean
                .palette?.colors?.cyan,
              opacity: ({ active }) =>
                active ? 1 : 0.5,
              cursor: "move",
            },
          }}
          x="name"
          y="date"
          size={10}
        />
      </VictoryChart>
      <VictoryChart
        theme={VictoryTheme.clean}
        horizontal
        {...sharedProps}
        padding={{
          top: 30,
          left: 50,
          right: 30,
          bottom: 0,
        }}
        scale={{ y: "time" }}
        height={120}
        containerComponent={
          <VictoryBrushContainer
            responsive={false}
            brushDomain={zoomDomain}
            brushDimension="y"
            onBrushDomainChange={
              handleZoom
            }
          />
        }
      >
        <VictoryAxis
          style={{
            axis: { stroke: "none" },
          }}
        />
        <VictoryAxis
          dependentAxis
          orientation="top"
          style={{
            axis: { stroke: "none" },
            tickLabels: {
              fontSize: 20,
            },
          }}
          tickCount={3}
          tickFormat={(t) =>
            t.getFullYear()
          }
        />
        <VictoryScatter
          data={points}
          size={5}
          style={{
            data: {
              fill: VictoryTheme.clean
                .palette?.colors?.cyan,
            },
          }}
          x="name"
          y="date"
        />
        <VictoryBar
          style={{
            data: {
              fill: VictoryTheme.clean
                .palette?.colors?.cyan,
            },
          }}
          data={bars}
          x="name"
          y={(d) => d.range[0]}
          y0={(d) => d.range[1]}
        />
      </VictoryChart>
    </div>
  );
}

render(<App />);
```
